[WC2008]游览计划

2020-03-05
WC

题意

在$n\cdot m$的网格中有$K$个关键点,打通非关键点需要一定费用

求把所有关键点按照四联通规则联通的最小费用

$n,m,K\leq 10$

题解

显然最佳答案一定是树形结构,可以称为斯坦纳树

令$f_{i,S}​$为集合中包含节点i包含关键点集合为S的最小费用

不同S之间的Dp不构成环,$f_{i,S}=f_{i,T}+f_{i,S-T}-a_i$

不同i之间的Dp构成环,$f_{i,S}=f_{j,S}+a_i$,用SPFA转移即可

输出方案记录每个状态的前驱,从某个关键点dfs即可

调试记录

dfs的时候从n*m-1开始了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#define MP make_pair
using namespace std;
const int INF = 0x3f3f3f3f;
int n, m, a[105], K = 0;
int f[105][(1 << 11)];
pair <int, int> pre[105][(1 << 11)];
queue <int> q; bool inq[105];
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
void SPFA(int R){
while (!q.empty()){
int cur = q.front(); q.pop(); inq[cur] = 0;
int x = cur / m, y = cur % m;
for (int d = 0; d < 4; d++){
int v = m * (x + dx[d]) + y + dy[d];
if (x + dx[d] < 0 || x + dx[d] >= n || y + dy[d] < 0 || y + dy[d] >= m) continue;
if (f[v][R] > f[cur][R] + a[v]){
f[v][R] = f[cur][R] + a[v];
pre[v][R] = MP(cur, R);
if (!inq[v]) q.push(v), inq[v] = 1;
}
}
}
}
int ans[105];
void dfs(int cur, int s){
if (!pre[cur][s].second) return;
ans[cur] = 1;
if (pre[cur][s].first == cur) dfs(cur, s ^ pre[cur][s].second);
dfs(pre[cur][s].first, pre[cur][s].second);
}
int main(){
freopen("1.in", "r", stdin);
scanf("%d%d", &n, &m); memset(f, 0x3f, sizeof f); int tmp;
for (int i = 0; i < n * m; i++){
scanf("%d", a + i);
if (a[i] == 0) f[i][(1 << (K++))] = 0, tmp = i;
}
for (int s = 1; s < (1 << K); s++){
for (int i = 0; i < n * m; i++){
for (int t = s & (s - 1); t; t = s & (t - 1)){
if (f[i][s] > f[i][t] + f[i][s ^ t] - a[i]){
f[i][s] = f[i][t] + f[i][s ^ t] - a[i];
pre[i][s] = MP(i, t);
}
}
if (f[i][s] != INF) q.push(i), inq[i] = 1;
}
SPFA(s);
} printf("%d\n", f[tmp][(1 << K) - 1]);
dfs(tmp, (1 << K) - 1);
for (int i = 0; i < n * m; i++){
if (a[i] == 0) putchar('x');
else if (ans[i]) putchar('o');
else putchar('_');
if ((i + 1) % m == 0) puts("");
}
return 0;
}